/**
 * 随机生成指定长度的字符串
 * @param {Number} len
 * @returns
 */
export function randomString(len = 16) {
  if (isNaN(len) || len < 1) return "";
  let res = "";
  for (let i = 0; i < len / 10; i++) {
    res += Math.random().toString(36).slice(2, 12).padEnd(10, "0");
  }
  return res.slice(0, len);
}

/**
 * 深拷贝对象
 * @param {Object} data
 * @returns
 */
export function deepCopy(data) {
  return JSON.parse(JSON.stringify(data));
}

/**
 * 格式化日期时间
 * @param {string} time
 * @param {string} pattern
 * @returns
 */
export function parseTime(time, pattern) {
  if (arguments.length === 0 || !time) {
    return null;
  }
  const format = pattern || "{y}-{m}-{d} {h}:{i}:{s}";
  let date;
  if (typeof time === "object") {
    date = time;
  } else {
    if (typeof time === "string" && /^[0-9]+$/.test(time)) {
      time = parseInt(time);
    } else if (typeof time === "string") {
      time = time
        .replace(new RegExp(/-/gm), "/")
        .replace("T", " ")
        .replace(new RegExp(/\.[\d]{3}/gm), "");
    }
    if (typeof time === "number" && time.toString().length === 10) {
      time = time * 1000;
    }
    date = new Date(time);
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay(),
  };
  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
    let value = formatObj[key];
    if (key === "a") {
      return ["日", "一", "二", "三", "四", "五", "六"][value];
    }
    if (result.length > 0 && value < 10) {
      value = "0" + value;
    }
    return value || 0;
  });
  return time_str;
}

/**
 * 人性化时间显示
 * @param {Object} datetime
 */
export function beautifyTime(datetime) {
  if (datetime == null) return "";

  datetime = datetime.replace(/-/g, "/");

  let time = new Date();
  let outTime = new Date(datetime);
  if (/^[1-9]\d*$/.test(datetime)) {
    outTime = new Date(
      datetime.length === 13 ? parseInt(datetime) : parseInt(datetime) * 1000
    );
  }

  if (
    time.getTime() < outTime.getTime() ||
    time.getFullYear() != outTime.getFullYear()
  ) {
    return parseTime(outTime, "{y}-{m}-{d} {h}:{i}");
  }

  if (time.getMonth() != outTime.getMonth()) {
    return parseTime(outTime, "{m}-{d} {h}:{i}");
  }

  if (time.getDate() != outTime.getDate()) {
    let day = outTime.getDate() - time.getDate();
    if (day == -1) {
      return parseTime(outTime, "昨天 {h}:{i}");
    }

    if (day == -2) {
      return parseTime(outTime, "前天 {h}:{i}");
    }

    return parseTime(outTime, "{m}-{d} {h}:{i}");
  }

  if (time.getHours() != outTime.getHours()) {
    return parseTime(outTime, "{h}:{i}");
  }

  let minutes = outTime.getMinutes() - time.getMinutes();
  if (minutes == 0) {
    return "刚刚";
  }

  minutes = Math.abs(minutes);
  return `${minutes}分钟前`;
}

/**
 * 列表转树
 * @param {Array} list 列表
 * @param {string} key 自定义主键
 * @param {string} pKey 自定义父主键
 */
export function list2tree(list, key = "id", pKey = "pid") {
  const res = [];
  const map = {};
  const l = deepCopy(list);
  l.map((e) => (map[e[key]] = e));
  l.forEach((e) => {
    let item = map[e[key]];
    let pItem = map[e[pKey]];
    if (pItem) {
      pItem.children = pItem.children || [];
      pItem.children.push(item);
    } else {
      res.push(item);
    }
  });
  return res;
}

/**
 * 树转列表
 * @param {Array} tree 树
 */
export function tree2list(tree) {
  const res = [];
  const queue = [...deepCopy(tree)];
  while (queue.length) {
    const item = queue.pop();
    res.push(item);
    if (item.children && item.children.length) {
      queue.push(...item.children);
    }
  }
  return res;
}

import SparkMD5 from "spark-md5";

/**
 * 分片计算文件md5
 * @param {File} file
 * @returns
 */
export function getFileMd5(file) {
  let pSize = 5 * 1024 * 1024; //分片大小
  return new Promise((resolve, reject) => {
    let fileReader = new FileReader();
    let spark = new SparkMD5.ArrayBuffer();

    let chunk = 0;
    fileReader.readAsArrayBuffer(file.slice(chunk, chunk + pSize));
    fileReader.onload = (e) => {
      spark.append(e.target.result);
      chunk += pSize;
      if (chunk < file.size) {
        fileReader.readAsArrayBuffer(file.slice(chunk, chunk + pSize));
      } else {
        resolve(spark.end());
      }
    };
    fileReader.onerror = function (e) {
      resolve({ code: "error", msg: e.target.error.message });
    };
  });
}

/**
 * 不丢失精度计算两个数的+-*\/
 * @param {String | Number} num1
 * @param {String | Number} num2
 * @param {String} calcStr 运算符
 * @returns
 */
export function calc(num1, num2, calcStr) {
  let str1, // 转换为字符串的数字
    str2,
    ws1 = 0, // ws1，ws2 用来存储传入的num的小数点后的数字的位数
    ws2 = 0, // 赋默认值，解决当整数和小数运算时倍数计算错误导致的结果误差
    bigger, // bigger和smaller用于加，减，除法找出小的那个数字，给后面补0，解决位数不对从而造成的计算错误的问题；乘法需要将结果除两个数字的倍数之和
    smaller, // 例如：加减除法中1.001 + 2.03 ，如果不给2.03进行补0，最后会变成1001+203，数字错位导致结果错误；乘法中1.12*1.1会放大为112*11，所以结果需要除以1000才会是正确的结果，112*11/1000=1.232
    zeroCount, // 需要补充0的个数
    isExistDot1, // 传入的数字是否存在小数点
    isExistDot2,
    sum,
    beishu = 1;
  // 将数字转换为字符串
  str1 = num1.toString();
  str2 = num2.toString();
  // 是否存在小数点（判断需要计算的数字是不是包含小数）
  isExistDot1 = str1.indexOf(".") != -1 ? true : false;
  isExistDot2 = str2.indexOf(".") != -1 ? true : false;
  // 取小数点后面的位数
  if (isExistDot1) {
    ws1 = str1.split(".")[1].length;
  }

  if (isExistDot2) {
    ws2 = str2.split(".")[1].length;
  }
  // 如ws1 和 ws2 无默认值，如果num1 或 num2 不是小数的话则 ws1 或 ws2 的值将为 undefined
  // bigger 和 smaller 的值会和预期不符
  bigger = ws1 > ws2 ? ws1 : ws2;
  smaller = ws1 < ws2 ? ws1 : ws2;

  switch (calcStr) {
    // 加减法找出小的那个数字，给后面补0，解决位数不对从而造成的计算错误的问题
    // 例如：1.001 + 2.03 ，如果不给2.03进行补0，最后会变成1001+203，数字错位导致结果错误
    case "+":
    case "-":
    case "/":
      zeroCount = bigger - smaller;
      for (var i = 0; i < zeroCount; i++) {
        if (ws1 == smaller) {
          str1 += "0";
        } else {
          str2 += "0";
        }
      }
      break;
    case "*":
      // 乘法需要将结果除两个数字的倍数之和
      bigger = bigger + smaller;
      break;
    default:
      return "暂不支持的计算类型，现已支持的有加法、减法、乘法、除法";
  }

  // 去除数字中的小数点
  str1 = str1.replace(".", "");
  str2 = str2.replace(".", "");

  // 计算倍数，例如：1.001小数点后有三位，则需要乘 1000 变成 1001，变成整数后精度丢失问题则不会存在
  for (var i = 0; i < bigger; i++) {
    beishu *= 10; // 等价于beishu = beishu * 10;
  }
  num1 = parseInt(str1);
  num2 = parseInt(str2);
  // 进行最终计算并除相应倍数
  switch (calcStr) {
    case "+":
      sum = (num1 + num2) / beishu;
      break;
    case "-":
      sum = (num1 - num2) / beishu;
      break;
    case "*":
      sum = (num1 * num2) / beishu;
      break;
    case "/":
      sum = num1 / num2;
      /* 除数与被除数同时放大一定倍数，不影响结果，
      所以对数字进行放大对应倍数并进行补0操作后不用另对倍数做处理 */
      break;
    default:
      return "暂不支持的计算类型，现已支持的有加法、减法、乘法、除法";
  }

  return sum;
}

/**
 * 创建一个支持重载的方法
 * Example:
 *    const func = createOverload()
 *    func.addImpl('string', 'number', () => {console.log('参数为字符串，数字的实现')})
 *    func.addImpl('string', 'string', () => {console.log('参数为字符串，字符串的实现')})
 */
export function createOverload() {
  const fnMap = new Map();
  function overload(...args) {
    const key = args.map((it) => typeof it).join(",");
    const fn = fnMap.get(key);
    if (!fn) {
      throw new TypeError("没有找到对应的实现");
    }
    return fn.apply(this, args);
  }
  overload.addImpl = function (...args) {
    const fn = args.pop();
    if (typeof fn != "function") {
      throw new TypeError("最后一个参数必须是方法");
    }
    const key = args.join(",");
    fnMap.set(key, fn);
  };
  return overload;
}

/**
 * 获取视频封面(第一帧图片)
 * @param {File} file 视频文件对象
 * @returns 图片base64
 */
export function getVideoCover(file) {
  return new Promise((resolve, reject) => {
    if (!file) {
      resolve({code: 'error', msg: '文件对象为空'})
    }
    const video = document.createElement('video');
    video.src = URL.createObjectURL(file);

    video.onloadeddata = () => {
      const canvas = document.createElement('canvas');
      const context = canvas.getContext('2d');

      // 设置 canvas 的尺寸与视频一致
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
	  
      // 设置视频时间为0
	    video.currentTime = 0;

      video.onseeked = () => {
        // 在 canvas 上绘制视频的第一帧
        context.drawImage(video, 0, 0, canvas.width, canvas.height);

        // 将 canvas 转换为 base64 格式的图片数据
        const thumbnailDataUrl = canvas.toDataURL('image/png');

        // 清理资源
        URL.revokeObjectURL(video.src);

        resolve(thumbnailDataUrl)
      };

    };

    // 在 video 加载失败时的处理
    video.onerror = function() {
      resolve({ code: "error", msg: "读取视频文件失败" });
    };
  })
}

/**
 * base64转file
 * @param {string} base64 
 * @param {string} filename 
 * @returns File对象
 */
export function base64toFile(base64, filename) {
  const arr = base64.split(',');
  const mime = arr[0].match(/:(.*?);/)[1];
  let bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
  while(n--){
      u8arr[n] = bstr.charCodeAt(n);
    }
	// 生成与图片后缀一样的文件名
	const extension = arr[0].match(/\/(.*?);/)[1];
	const autoGeneratedFilename = filename || `file_${Date.now()}.${extension}`;
  return new File([u8arr], autoGeneratedFilename, {type:mime});
}

/**
 * 格式化文件大小
 * @param {Number} size 文件大小   B
 * @returns 结果
 */
export function formatFileSize(size) {
  if (size < 1024) {
    return size + "B";
  } else if (size < 1024 * 1024) {
    return parseInt((size / 1024) * 100) / 100 + "KB";
  } else if (size < 1024 * 1024 * 1024) {
    return parseInt((size / (1024 * 1024)) * 100) / 100 + "MB";
  } else {
    return parseInt((size / (1024 * 1024 * 1024)) * 100) / 100 + "GB";
  }
}